/*
 * Copyright (c) 2009-2010 Microelectronic Center, 
 * Harbin Institute of Technology
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gou Pengfei
 */

#include "arch/faults.hh"
#include "arch/utility.hh"
#include "base/cp_annotate.hh"
#include "base/cprintf.hh"
#include "base/inifile.hh"
#include "base/loader/symtab.hh"
#include "base/misc.hh"
#include "base/pollevent.hh"
#include "base/range.hh"
#include "base/stats/events.hh"
#include "base/trace.hh"
#include "base/types.hh"
#include "config/the_isa.hh"
#include "cpu/base.hh"
#include "cpu/exetrace.hh"
#include "cpu/profile.hh"

#include "cpu/edge/atomic/impl.hh"
#include "cpu/edge/atomic/atomic.hh"
#include "cpu/edge/atomic/atomic_dyn_inst.hh"
#include "cpu/edge/atomic/base.hh"

#include "cpu/simple_thread.hh"
#include "cpu/smt.hh"
#include "cpu/edge/static_inst.hh"
#include "cpu/thread_context.hh"
#include "mem/packet.hh"
#include "mem/request.hh"
#include "params/AtomicEdgeCPU.hh"
#include "sim/byteswap.hh"
#include "sim/debug.hh"
#include "sim/sim_events.hh"
#include "sim/sim_object.hh"
#include "sim/stats.hh"
#include "sim/system.hh"

#include "mem/mem_object.hh"

using namespace std;
using namespace TheISA;

template<class Impl>
BaseAtomicEdgeCPU<Impl>::BaseAtomicEdgeCPU(AtomicEdgeCPUParams *p)
    : BaseCPU(p), thread(NULL)
{
    thread = new SimpleThread(this, 0, p->workload[0],
                              p->itb, p->dtb);

    thread->setStatus(ThreadContext::Halted);

    tc = thread->getTC();

    edgeTracer = p->edge_tracer;

    numInst = 0;
    startNumInst = 0;
    numLoad = 0;
    startNumLoad = 0;
    lastIcacheStall = 0;
    lastDcacheStall = 0;
    globalBlockID = 0;
    instSize = sizeof(TheISA::MachInst);
    maxPass = p->maxPass;
    readQueueSize = p->readQueueSize;
    writeQueueSize = p->writeQueueSize;
    ldstQueueSize = p->ldstQueueSize;
    instQueueSize = p->instQueueSize;

    // Preparing data structures ...
    instQueue.resize(instQueueSize);
    readQueue.resize(readQueueSize);
    writeQueue.resize(writeQueueSize);
    ldstQueue.resize(ldstQueueSize);

    predicationStatus.reset();

    testInstPath.reserve(TheISA::MaxInstsInBlock);
    testInstPath.resize(0);
    testInstPath.clear();

    // Memory blocker is defaulted to MAXIMUM.
    memBlockerLSID = 0xFFFF;

    threadContexts.push_back(tc);

#ifndef NDEBUG
    instCount = 0;
    blockcount = 0;
#endif
}

template<class Impl>
BaseAtomicEdgeCPU<Impl>::~BaseAtomicEdgeCPU()
{
}

template<class Impl>
void
BaseAtomicEdgeCPU<Impl>::deallocateContext(int thread_num)
{
    // for now, these are equivalent
    suspendContext(thread_num);
}


template<class Impl>
void
BaseAtomicEdgeCPU<Impl>::haltContext(int thread_num)
{
    // for now, these are equivalent
    suspendContext(thread_num);
}

template<class Impl>
void
BaseAtomicEdgeCPU<Impl>::regStats()
{
    using namespace Stats;

    BaseCPU::regStats();

    numInsts
        .name(name() + ".num_insts")
        .desc("Number of instructions executed")
        ;

    numMemRefs
        .name(name() + ".num_refs")
        .desc("Number of memory references")
        ;

    notIdleFraction
        .name(name() + ".not_idle_fraction")
        .desc("Percentage of non-idle cycles")
        ;

    idleFraction
        .name(name() + ".idle_fraction")
        .desc("Percentage of idle cycles")
        ;

    icacheStallCycles
        .name(name() + ".icache_stall_cycles")
        .desc("ICache total stall cycles")
        .prereq(icacheStallCycles)
        ;

    dcacheStallCycles
        .name(name() + ".dcache_stall_cycles")
        .desc("DCache total stall cycles")
        .prereq(dcacheStallCycles)
        ;

    icacheRetryCycles
        .name(name() + ".icache_retry_cycles")
        .desc("ICache total retry cycles")
        .prereq(icacheRetryCycles)
        ;

    dcacheRetryCycles
        .name(name() + ".dcache_retry_cycles")
        .desc("DCache total retry cycles")
        .prereq(dcacheRetryCycles)
        ;

    idleFraction = constant(1.0) - notIdleFraction;
    
    fetchedInsts
        .name(name() + ".INST:Insts")
        .desc("Number of instructions fetch has processed")
        .prereq(fetchedInsts);

    fetchedBlocks
        .name(name() + ".InstBLOCK:blocks")
        .desc("Number of inst blocks fetch has processed")
        .prereq(fetchedBlocks);

    fetchedChunks
        .name(name() + ".InstBLOCK:chunks")
        .desc("Number of inst chunks fetch has processed")
        .prereq(fetchedChunks);

    fetchedNopInsts
        .name(name() + ".INST:Nops")
        .desc("Number of NOPs that fetch encountered")
        .prereq(fetchedNopInsts);

    fetchedHeadNopInsts
        .name(name() + ".INST:HeadNops")
        .desc("Number of NOPS in header")
        .prereq(fetchedHeadNopInsts);
        
    fetchedConstInsts
        .name(name() + ".INST:Consts")
        .desc("Number of Insts that generate constants")
        .prereq(fetchedConstInsts);

    fetchedMovInsts
        .name(name() + ".INST:Movs")
        .desc("Number of movs that fetch has encountered")
        .prereq(fetchedMovInsts);
        
    fetchedTestInsts
        .name(name() + ".INST:Tests")
        .desc("Number of tests that fetch has encountered")
        .prereq(fetchedTestInsts);

    fetchedControls
        .name(name() + ".INST:Controls")
        .desc("Number of Controls that fetch has encountered")
        .prereq(fetchedControls);

    fetchedDirectControls
        .name(name() + ".INST:DirectControls")
        .desc("Number of Direct Controls that fetch has encountered")
        .prereq(fetchedDirectControls);

    fetchedIndirectControls
        .name(name() + ".INST:IndirectControls")
        .desc("Number of Indirect Controls that fetch has encountered")
        .prereq(fetchedIndirectControls);
        
    fetchedNullifies
        .name(name() + ".INST:Nullifies")
        .desc("Number of Nullifies that fetch has encountered")
        .prereq(fetchedNullifies);

    fetchedMemRefs
        .name(name() + ".INST:MemRefs")
        .desc("Number of Memory References that fetch has encountered")
        .prereq(fetchedMemRefs);

    fetchedLoads
        .name(name() + ".INST:Loads")
        .desc("Number of Loads that fetch has encountered")
        .prereq(fetchedLoads);

    fetchedStores
        .name(name() + ".INST:Stores")
        .desc("Number of Stores that fetch has encountered")
        .prereq(fetchedStores);

    fetchedWrites
        .name(name() + ".INST:Writes")
        .desc("Number of Writes that fetch has encountered")
        .prereq(fetchedWrites);        

    fetchedReads
        .name(name() + ".INST:Reads")
        .desc("Number of Reads that fetch has encountered")
        .prereq(fetchedReads);

    fetchedReadWrites
        .name(name() + ".INST:ReadWrites")
        .desc("Number of ReadWrites that fetch has encountered")
        .prereq(fetchedReadWrites);

    fetchedPredOnTrues
        .name(name() + ".INST:PredOnTrues")
        .desc("Number of Predication-on-true that fetch has encountered")
        .prereq(fetchedPredOnTrues);

    fetchedPredOnFalses
        .name(name() + ".INST:PredOnFalses")
        .desc("Number of Predication-on-false that fetch has encountered")
        .prereq(fetchedPredOnFalses);

    fetchedNopInstsRatio
        .init(0,1,0.1)
        .name(name() + ".InstBLOCK:NopsRatio")
        .desc("Number of NOPs that fetch encountered")
        .precision(4)
        .flags(Stats::cdf);

    fetchedHeadNopInstsRatio
        .init(0,1,0.1)
        .name(name() + ".InstBLOCK:HeadNopsRatio")
        .desc("Number of NOPS in header")
        .precision(4)
        .flags(Stats::cdf);
        
    fetchedConstInstsRatio
        .init(0,1,0.1)
        .name(name() + ".InstBLOCK:ConstsRatio")
        .desc("Number of Insts that generate constants")
        .precision(4)
        .flags(Stats::cdf);

    fetchedMovInstsRatio
        .init(0,1,0.1)
        .name(name() + ".InstBLOCK:MovsRatio")
        .desc("Number of movs that fetch has encountered")
        .precision(4)
        .flags(Stats::cdf);
        
    fetchedTestInstsRatio
        .init(0,1,0.1)
        .name(name() + ".InstBLOCK:TestsRatio")
        .desc("Number of tests that fetch has encountered")
        .precision(4)
        .flags(Stats::cdf);

    fetchedControlsRatio
        .init(0,1,0.1)
        .name(name() + ".InstBLOCK:ControlsRatio")
        .desc("Number of Controls that fetch has encountered")
        .precision(4)
        .flags(Stats::cdf);

    fetchedDirectControlsRatio
        .init(0,1,0.1)
        .name(name() + ".InstBLOCK:DirectControlsRatio")
        .desc("Number of Direct Controls that fetch has encountered")
        .precision(4)
        .flags(Stats::cdf);

    fetchedIndirectControlsRatio
        .init(0,1,0.1)
        .name(name() + ".InstBLOCK:IndirectControlsRatio")
        .desc("Number of Indirect Controls that fetch has encountered")
        .precision(4)
        .flags(Stats::cdf);
        
    fetchedNullifiesRatio
        .init(0,1,0.1)
        .name(name() + ".InstBLOCK:NullifiesRatio")
        .desc("Number of Nullifies that fetch has encountered")
        .precision(4)
        .flags(Stats::cdf);

    fetchedMemRefsRatio
        .init(0,1,0.1)
        .name(name() + ".InstBLOCK:MemRefsRatio")
        .desc("Number of Memory References that fetch has encountered")
        .precision(4)
        .flags(Stats::cdf);

    fetchedLoadsRatio
        .init(0,1,0.1)
        .name(name() + ".InstBLOCK:LoadsRatio")
        .desc("Number of Loads that fetch has encountered")
        .precision(4)
        .flags(Stats::cdf);

    fetchedStoresRatio
        .init(0,1,0.1)
        .name(name() + ".InstBLOCK:StoresRatio")
        .desc("Number of Stores that fetch has encountered")
        .precision(4)
        .flags(Stats::cdf);

    fetchedWritesRatio
        .init(0,1,0.1)
        .name(name() + ".InstBLOCK:WritesRatio")
        .desc("Number of Writes that fetch has encountered")
        .precision(4)
        .flags(Stats::cdf);   

    fetchedReadsRatio
        .init(0,1,0.1)
        .name(name() + ".InstBLOCK:ReadsRatio")
        .desc("Number of Reads that fetch has encountered")
        .precision(4)
        .flags(Stats::cdf);

    fetchedReadWritesRatio
        .init(0,1,0.1)
        .name(name() + ".InstBLOCK:ReadWritesRatio")
        .desc("Number of ReadWrites that fetch has encountered")
        .precision(4)
        .flags(Stats::cdf);

    fetchedPredOnTruesRatio
        .init(0,1,0.1)
        .name(name() + ".InstBLOCK:PredOnTruesRatio")
        .desc("Number of Predication-on-true that fetch has encountered")
        .precision(4)
        .flags(Stats::cdf);

    fetchedPredOnFalsesRatio
        .init(0,1,0.1)
        .name(name() + ".InstBLOCK:PredOnFalsesRatio")
        .desc("Number of Predication-on-false that fetch has encountered")
        .precision(4)
        .flags(Stats::cdf);

    fetchedNumConsumers
        .init(0,EDGEStaticInst::MaxInstConsumers,1)
        .name(name() + ".INST:NumConsumers")
        .desc("Number of consumers of insts  that fetch has encountered")
        .flags(Stats::cdf);

    fetchedNumOperands
        .init(0,EDGEStaticInst::MaxInstOperands,1)
        .name(name() + ".INST:NumOperands")
        .desc("Number of operands of insts  that fetch has encountered")
        .flags(Stats::cdf);

    fetchedInstBlockSize
        .init(0,TheISA::MaxInstsInBlock,32)
        .name(name() + ".InstBLOCK:InstBlockSize")
        .desc("Number of insts of each block")
        .flags(Stats::cdf);
        
    iqOperandsPropagated
        .name(name() + ".iqOperandsPropagated")
        .desc("Number of operands propagated.")
        .prereq(iqOperandsPropagated);

    iqNullTokenPropagated
        .name(name() + ".iqNullTokenPropagated")
        .desc("Number of null token propagated.")
        .prereq(iqNullTokenPropagated);

    iqExceptTokenPropagated
        .name(name() + ".iqExceptTokenPropagated")
        .desc("Number of  except token propagated.")
        .prereq(iqExceptTokenPropagated);

    iqPredicationPropagated
        .name(name() + ".iqPredicationPropagated")
        .desc("Number of  predication propagated.")
        .prereq(iqPredicationPropagated);
}

template<class Impl>
void
BaseAtomicEdgeCPU<Impl>::resetStats()
{
     notIdleFraction = (_status != Idle);
}

template<class Impl>
void
BaseAtomicEdgeCPU<Impl>::serialize(ostream &os)
{
    SERIALIZE_ENUM(_status);
    BaseCPU::serialize(os);
    nameOut(os, csprintf("%s.xc.0", name()));
    thread->serialize(os);
}

template<class Impl>
void
BaseAtomicEdgeCPU<Impl>::unserialize(Checkpoint *cp, const string &section)
{
    UNSERIALIZE_ENUM(_status);
    BaseCPU::unserialize(cp, section);
    thread->unserialize(cp, csprintf("%s.xc.0", section));
}

template<class Impl>
void
change_thread_state(ThreadID tid, int activate, int priority)
{
}

template<class Impl>
Fault
BaseAtomicEdgeCPU<Impl>::copySrcTranslate(Addr src)
{
    panic("Unimplemented func: copySrcTranslate()\n");
    return NoFault;
}

template<class Impl>
Fault
BaseAtomicEdgeCPU<Impl>::copy(Addr dest)
{
    panic("copy not implemented");
    return NoFault;
}

template<class Impl>
void
BaseAtomicEdgeCPU<Impl>::checkForInterrupts()
{
    panic("Unimplemented func: checkForInterrupts()\n");
}

template<class Impl>
int
BaseAtomicEdgeCPU<Impl>::wakeDependences(DynInstPtr &completed_inst)
{
    int dependents = 0;

    DPRINTF(AtomicEdgeCPU, "----------------"
        "Waking dependents of completed"
        " instruction[Bid:%lli][Iid:%lli].----------------\n",
        completed_inst->getBlockID(), 
        completed_inst->getInstID() );

   // Dump exec trace
   if (completed_inst->traceData) {
        completed_inst->traceData->dump();
        delete completed_inst->traceData;
        completed_inst->traceData = NULL;
   }

    assert(!completed_inst->isSquashed());

    if (completed_inst->isStore() ||
        completed_inst->isControl()) {

        DPRINTF(AtomicEdgeCPU,"Store or control encountered, "
            "no consumer will be waken.\n");

        return dependents;
    }

    DynInstPtr consumer;
    OpSize result = 0;

    for (int consumer_idx = 0;
         consumer_idx < completed_inst->getNumConsumers();
         consumer_idx ++)
    {
        dependents ++;

        // Insts with predication slot may received 
        // multiple predications among which only
        // one is matched. If there is a false 
        // predication received, we should avoid
        // add this inst into ready list if the inst
        // has been added before.
        bool add_to_ready_list = true;

        uint8_t type =
            completed_inst->getConsumerType(consumer_idx);

        DPRINTF(AtomicEdgeCPU, "Waking any dependents on"
            " consumer[idx:%i][type:%i][id:%i].\n",
            (int)consumer_idx,
            (int)completed_inst->getConsumerType(consumer_idx),
            (int)completed_inst->getConsumerID(consumer_idx));

        if (type == TheISA::WriteSlotOrNoTarget) {

            uint8_t sub_type =
                completed_inst->getConsumerSubType(consumer_idx);

            if (sub_type == TheISA::WriteSlot) {

                int write_idx =  completed_inst->getConsumerID(consumer_idx);
                
                assert(write_idx >= 0 && write_idx < writeQueue.size());
                // Wakeup write inst
                consumer = writeQueue[write_idx];
                assert(consumer->isWrite());

                // Operand of write-reg inst is defaulted to 0
                consumer->setIntOperand(0,
                                        completed_inst->getIntResult() );
                // Mark inst as receiving a operand
                consumer->markOperandReady();

            } else {
                // No target, continue.
                continue;
            }
        } else if (type == TheISA::PredSlot) {
        
            int iq_idx = completed_inst->getConsumerID(consumer_idx);
                                                         
            assert(iq_idx >= 0 && iq_idx < instQueue.size());
            // Wakeup insts.
            consumer = instQueue[iq_idx];

            uint64_t data = completed_inst->getIntResult();
              
             // If an exception token is combined with 
             // a predication, the predicate value should 
             // always be set as false.
            if (completed_inst->getDataflowTokenType()
                == TheISA::Exception) {
                data = 0;
            }
            
            if (data == 0) {
                add_to_ready_list = 
                    consumer->setPredStatus(TheISA::PredFalse);
            } else {
                add_to_ready_list = 
                    consumer->setPredStatus(TheISA::PredTrue);
            }

            // Mark inst as receiving a pred
            consumer->markPredReady();

            iqPredicationPropagated ++;

        } else {

            assert(type == TheISA::Operand0 || type == TheISA::Operand1);
            
            int iq_idx = completed_inst->getConsumerID(consumer_idx);
                                                         
            assert(iq_idx >=0 && iq_idx < instQueue.size());

            result = completed_inst->getIntResult();
            consumer = instQueue[iq_idx];

            if (type == TheISA::Operand0) {
                consumer->setIntOperand(0, result);
            } else if ( type == TheISA::Operand1 ){
                consumer->setIntOperand(1, result);
            } else {
                panic("Unrecogonized operand number\n");
            }

            // Mark inst as receiving a operand
            consumer->markOperandReady();
        }

        iqOperandsPropagated ++;

        DPRINTF(AtomicEdgeCPU, "Consumer[Bid:%lli][Iid:%lli].\n",
                               consumer->getBlockID(), consumer->getInstID());

        // Producer and consumer should be in the same inst block.
        assert(completed_inst->getBlockID() == consumer->getBlockID());

        // Read register is the head of a inst block, so it
        // has no token to propagate.
        if (!completed_inst->isRead()) {
            // If the data flow token type of this producer is Nullification,
            // set the consumer to be nullified. Propagating the token.
            if (completed_inst->getDataflowTokenType()
                        == TheISA::Nullification ) {

                DPRINTF(AtomicEdgeCPU, "Nullify consumer[Bid:%lli][Iid:%lli] @PC %lli.\n",
                    consumer->getBlockID(),
                    consumer->getInstID(),
                    consumer->readPC() );

                //assert(type != TheISA::PredSlot);
                // If a null token is combined with
                // the predication, this null token
                // will be completely ignored.
                if (type != TheISA::PredSlot) {
                    consumer->setNullified();
                    consumer->setDataflowTokenType(TheISA::Nullification);

                    iqNullTokenPropagated ++;
                }

            } else if (completed_inst->getDataflowTokenType()
                        == TheISA::Exception) {

                    Fault fault = completed_inst->getFault();

                    assert(fault!= NoFault);

                    DPRINTF(AtomicEdgeCPU, "Propagate fault %s from"
                        " producer[Bid%lli][Iid%lli] to consumer[Bid%lli][Iid%lli].\n",
                        fault->name(), completed_inst->getBlockID(),
                        completed_inst->getInstID(),
                        consumer->getBlockID(), consumer->getInstID());

                    consumer->fault = fault;
                    //Propagate the fault
                    consumer->setDataflowTokenType(TheISA::Exception);

                    iqExceptTokenPropagated ++;
            }
        }

        DPRINTF(AtomicEdgeCPU, "Inst ID of consumer inst is %lli "
            "while Consumer ID of complete inst is %lli\n",
            consumer->getInstID(),
            completed_inst->getConsumerID(consumer_idx));

        // Check if this inst can be issued.
        // If the consumer is register write, it will be 
        // executed in commit. As a result, do not 
        // add register write to readyList but just 
        // inform the instruction block that a write 
        // has been received.
        if (add_to_ready_list && consumer->readyToIssue()) {

            if (!consumer->isWrite()) {
            
                readyList.push_back(consumer->getInstID());
                
            } else {
                DPRINTF(AtomicEdgeCPU, "Write[Wid:%i] is waken.\n", 
                    consumer->getInstID());
                    
                curEdgeBlockPtr->incReceivedWrite();
                readyWrites.push_back(consumer->getInstID());

                Fault fault = consumer->fault;
                if (fault != NoFault) {
                    DPRINTF(AtomicEdgeCPU, "Write has fault.\n");
                    curEdgeBlockPtr->setFault(fault);
                }
            }
        }
    }

    return dependents;
}

template<class Impl>
void
BaseAtomicEdgeCPU<Impl>::resetQueues()
{
    // Reset instruction queue.
    for (int i = 0; i < instQueueSize; i++) {
        instQueue[i] = NULL;
    }

    // Reset ld/st queue
    for (int i = 0; i < ldstQueueSize; i++) {
        ldstQueue[i].reset();
    }

    // Reset read/write queues
    for (int i = 0; i < readQueueSize; i++) {
        readQueue[i] = NULL;
    }

    for (int i = 0; i < writeQueueSize; i++) {
        writeQueue[i] = NULL;
    }
}

template<class Impl>
void
BaseAtomicEdgeCPU<Impl>::setupFetchRequest(Request *req)
{
    Addr fetchPC = thread->readPC()& PCMask;

   // Addr fetchPC = (threadPC & PCMask) + fetchOffset;

    // We will get a chunk per fetch
    req->setVirt(0, fetchPC, TheISA::ChunkSize, 
        Request::INST_FETCH, fetchPC);
}

template<class Impl>
void
BaseAtomicEdgeCPU<Impl>::constructHeader(TheISA::MachInst *inst_chunk)
{
    assert(curEdgeBlockPtr);
    
    HeaderInfo header_info;
    header_info.resize(TheISA::HeaderInfoSize);

    // Extract info from the header chunk.
    // This is extremely dependent on 
    // TRIPS ISA.
    for (int offset=0;offset < TheISA::ChunkSizeInWords;offset++) {
    
        TheISA::MachInst inst_code = gtoh(inst_chunk[offset]);
        
        header_info[TheISA::HeaderInfoSize - 1-(offset*4)/32] 
            |=( mbits(inst_code, 31, 28) >> (( offset*4) % 32 ));
    }

    if (!curEdgeBlockPtr->depackHeader(header_info)) {
        panic("Looks header info goes into panic.\n");
    }
}

template<class Impl>
void
BaseAtomicEdgeCPU<Impl>::advancePC()
{
    assert(curEdgeBlockPtr);
    Addr next_block_pc = curEdgeBlockPtr->getBranchTarget();
    
    DPRINTF(AtomicEdgeCPU, "Advance PC to %#x.\n", next_block_pc);
    
    // go to the next instruction block.
    thread->setBlockPC(next_block_pc);
}

template<class Impl>
void
BaseAtomicEdgeCPU<Impl>::updateMemBlockerLSID()
{
    // The basic rule to set a memory blocker 
    // is to find the first unexecuted store.

    DPRINTF(AtomicEdgeCPU, "Updating memory blocker from [LSID:%i] "
        "to %i.\n", memBlockerLSID, curEdgeBlockPtr->getMemBlockerLSID());

    memBlockerLSID = curEdgeBlockPtr->getMemBlockerLSID();
}

template<class Impl>
void
BaseAtomicEdgeCPU<Impl>::updateInstStats(DynInstPtr &inst)
{
    assert(inst->getBlockID() == curEdgeBlockPtr->getBlockID());
    assert(!inst->isNop());

    DPRINTF(AtomicEdgeCPU,"Preparing inst[Bid:%lli][Iid:%lli]@%#x"
        ": %s.\n", inst->getBlockID(),
        inst->getInstID(),
        inst->readPC(),
        inst->staticInst->disassemble(inst->readPC()));
    
    fetchedInsts ++;

    curEdgeBlockPtr->numTotalInsts++;

    if (inst->isMemRef()) {

        fetchedMemRefs ++;
        curEdgeBlockPtr->numMemRefs ++;

        if (inst->isStore()) {
            fetchedStores ++;
            curEdgeBlockPtr->numStores ++;
        } else {
            fetchedLoads ++;
            curEdgeBlockPtr->numLoads ++;
        }

    } else if (inst->isControl()) {
        fetchedControls ++;
        curEdgeBlockPtr->numControlInsts ++;
        
        if (inst->isDirectCtrl()) {
            fetchedDirectControls ++;
            curEdgeBlockPtr->numDirectControls ++;
        } else if(inst->isIndirectCtrl()) {
            fetchedIndirectControls ++;
            curEdgeBlockPtr->numIndirectControls ++;
        }
    } else if (inst->isWrite()) {
    
        fetchedWrites ++;
        curEdgeBlockPtr->numWrites ++;
        
    } else if (inst->isRead()) {

        fetchedReads ++;
        curEdgeBlockPtr->numReads ++;

    } else {
        if (inst->isNullify()) {
            fetchedNullifies ++;
            curEdgeBlockPtr->numNullifies ++;
        } else if(inst->isMove()) {
            fetchedMovInsts++;
            curEdgeBlockPtr->numMoves ++;
        } else if (inst->isTest()) {
            fetchedTestInsts++;
            curEdgeBlockPtr->numTestInsts ++;
        } else if (inst->isConst()) {
            fetchedConstInsts++;
            curEdgeBlockPtr->numConstInsts ++;
        }
    }

    if (inst->staticInst->getPredication()
        == TheISA::PredUponFalse) {
        
        fetchedPredOnFalses ++;
        curEdgeBlockPtr->numPredOnFalses ++;
        
    } else if (inst->staticInst->getPredication()
        == TheISA::PredUponTrue) {
        
        fetchedPredOnTrues ++;
        curEdgeBlockPtr->numPredOnTrues ++;
        
    }

    fetchedNumConsumers.sample(inst->getNumConsumers());
    fetchedNumOperands.sample(inst->getNumOperands());

}

template<class Impl>
void
BaseAtomicEdgeCPU<Impl>::updateInstBlockStats(EdgeBlockPtr &inst_block)
{
    uint32_t block_size = inst_block->numTotalInsts;

    fetchedInstBlockSize.sample(block_size);

    //assert(block_size > TheISA::HeaderSize);

    double block_size_f = (inst_block->getNextBlockPC() - 
        inst_block->getStartPC())/ instSize;
    
    double nop_ratio = 
        ((double)(inst_block->numNopInsts-inst_block->numHeadNops)) 
        / block_size_f;        
    double head_nop_ratio = 
        ((double)inst_block->numHeadNops )/ (double)TheISA::HeaderSize;
    double move_ratio = 
        ((double)inst_block->numMoves) / block_size_f;
    double test_ratio = 
        ((double)inst_block->numTestInsts) / block_size_f;
    double const_ratio = 
        ((double)inst_block->numConstInsts) / block_size_f;
    double nullify_ratio = 
        ((double)inst_block->numNullifies) / block_size_f;
    double control_ratio = 
        ((double)inst_block->numControlInsts) / block_size_f;
    double direct_control_ratio = 
        ((double)inst_block->numDirectControls) / block_size_f;
    double indirect_control_ratio = 
        ((double)inst_block->numIndirectControls) / block_size_f;
    double write_ratio = 
        ((double)inst_block->numWrites) / (double)TheISA::HeaderSize;
    double read_ratio = 
        ((double)inst_block->numReads) / (double)TheISA::HeaderSize;
    //double read_write_ratio = 
        //((double)inst_block->numReadWrites) / (double)TheISA::HeaderSize;
    double mem_ref_ratio = 
        ((double)inst_block->numMemRefs) / block_size_f;
    double store_ratio = 
        ((double)inst_block->numStores) / block_size_f;
    double load_ratio = 
        ((double)inst_block->numLoads) / block_size_f;
    double pred_on_false_ratio = 
        ((double)inst_block->numPredOnFalses) / block_size_f;
    double pred_on_true_ratio = 
        ((double)inst_block->numPredOnTrues) / block_size_f;
    
    fetchedNopInstsRatio.sample(nop_ratio);
    fetchedHeadNopInstsRatio.sample(head_nop_ratio);
    fetchedMovInstsRatio.sample(move_ratio);
    fetchedTestInstsRatio.sample(test_ratio);
    fetchedConstInstsRatio.sample(const_ratio);
    fetchedNullifiesRatio.sample(nullify_ratio);
    fetchedControlsRatio.sample(control_ratio);
    fetchedDirectControlsRatio.sample(direct_control_ratio);
    fetchedIndirectControlsRatio.sample(indirect_control_ratio);
    fetchedWritesRatio.sample(write_ratio);
    fetchedReadsRatio.sample(read_ratio);
    //fetchedReadWritesRatio.sample(read_write_ratio);
    fetchedMemRefsRatio.sample(mem_ref_ratio);
    fetchedStoresRatio.sample(store_ratio);
    fetchedLoadsRatio.sample(load_ratio);
    fetchedPredOnFalsesRatio.sample(pred_on_false_ratio);
    fetchedPredOnTruesRatio.sample(pred_on_true_ratio);
    
}

template class BaseAtomicEdgeCPU<AtomicEdgeCPUImpl>;

